home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / Mesa-3.0 / SRC-GLUT / glut_gamemode.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-26  |  16.0 KB  |  676 lines

  1.  
  2. /* Copyright (c) Mark J. Kilgard, 1998. */
  3.  
  4. /* This program is freely distributable without licensing fees
  5.    and is provided without guarantee or warrantee expressed or
  6.    implied. This program is -not- in the public domain. */
  7.  
  8. #include <assert.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12.  
  13. #include "glutint.h"
  14.  
  15. #ifndef _WIN32
  16. #include <X11/Xlib.h>
  17. #include <X11/Xatom.h>
  18.  
  19. /* SGI optimization introduced in IRIX 6.3 to avoid X server
  20.    round trips for interning common X atoms. */
  21. #if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
  22. #include <X11/SGIFastAtom.h>
  23. #else
  24. #define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
  25. #endif
  26. #endif  /* not _WIN32 */
  27.  
  28. int __glutDisplaySettingsChanged = 0;
  29. static DisplayMode *dmodes, *currentDm = NULL;
  30. static int ndmodes = -1;
  31. GLUTwindow *__glutGameModeWindow = NULL;
  32.  
  33. #ifdef TEST
  34. static char *compstr[] =
  35. {
  36.   "none", "=", "!=", "<=", ">=", ">", "<", "~"
  37. };
  38. static char *capstr[] =
  39. {
  40.   "width", "height", "bpp", "hertz", "num"
  41. };
  42. #endif
  43.  
  44. void CDECL
  45. __glutCloseDownGameMode(void)
  46. {
  47.   if (__glutDisplaySettingsChanged) {
  48. #ifdef _WIN32
  49.     /* Assumes that display settings have been changed, that
  50.        is __glutDisplaySettingsChanged is true. */
  51.     ChangeDisplaySettings(NULL, 0);
  52. #endif
  53.     __glutDisplaySettingsChanged = 0;
  54.   }
  55.   __glutGameModeWindow = NULL;
  56. }
  57.  
  58. void APIENTRY
  59. glutLeaveGameMode(void)
  60. {
  61.   if (__glutGameModeWindow == NULL) {
  62.     __glutWarning("not in game mode so cannot leave game mode");
  63.     return;
  64.   }
  65.   __glutDestroyWindow(__glutGameModeWindow,
  66.     __glutGameModeWindow);
  67.   __glutGameModeWindow = NULL;
  68. }
  69.  
  70. #ifdef _WIN32
  71.  
  72. /* Same values as from MSDN's SetDisp.c example. */
  73. #define MIN_WIDTH 400
  74. #define MIN_FREQUENCY 60
  75.  
  76. static void
  77. initGameModeSupport(void)
  78. {
  79.   DEVMODE dm;
  80.   DWORD mode;
  81.   int i;
  82.  
  83.   if (ndmodes >= 0) {
  84.     /* ndmodes is initially -1 to indicate no
  85.        dmodes allocated yet. */
  86.     return;
  87.   }
  88.  
  89.   /* Determine how many display modes there are. */
  90.   ndmodes = 0;
  91.   mode = 0;
  92.   while (EnumDisplaySettings(NULL, mode, &dm)) {
  93.     if (dm.dmPelsWidth >= MIN_WIDTH &&
  94.       (dm.dmDisplayFrequency == 0 ||
  95.       dm.dmDisplayFrequency >= MIN_FREQUENCY)) {
  96.       ndmodes++;
  97.     }
  98.     mode++;
  99.   }
  100.  
  101.   /* Allocate memory for a list of all the display modes. */
  102.   dmodes = (DisplayMode*)
  103.     malloc(ndmodes * sizeof(DisplayMode));
  104.  
  105.   /* Now that we know how many display modes to expect,
  106.      enumerate them again and save the information in
  107.      the list we allocated above. */
  108.   i = 0;
  109.   mode = 0;
  110.   while (EnumDisplaySettings(NULL, mode, &dm)) {
  111.     /* Try to reject any display settings that seem unplausible. */
  112.     if (dm.dmPelsWidth >= MIN_WIDTH &&
  113.       (dm.dmDisplayFrequency == 0 ||
  114.       dm.dmDisplayFrequency >= MIN_FREQUENCY)) {
  115.       dmodes[i].devmode = dm;
  116.       dmodes[i].valid = 1;  /* XXX Not used for now. */
  117.       dmodes[i].cap[DM_WIDTH] = dm.dmPelsWidth;
  118.       dmodes[i].cap[DM_HEIGHT] = dm.dmPelsHeight;
  119.       dmodes[i].cap[DM_PIXEL_DEPTH] = dm.dmBitsPerPel;
  120.       if (dm.dmDisplayFrequency == 0) {
  121.      /* Guess a reasonable guess. */
  122.     /* Lame Windows 95 version of EnumDisplaySettings. */
  123.         dmodes[i].cap[DM_HERTZ] = 60;
  124.       } else {
  125.     dmodes[i].cap[DM_HERTZ] = dm.dmDisplayFrequency;
  126.       }
  127.       i++;
  128.     }
  129.     mode++;
  130.   }
  131.  
  132.   assert(i == ndmodes);
  133. }
  134.  
  135. #else
  136.  
  137. /* X Windows version of initGameModeSupport. */
  138. static void
  139. initGameModeSupport(void)
  140. {
  141.   if (ndmodes >= 0) {
  142.     /* ndmodes is initially -1 to indicate no
  143.        dmodes allocated yet. */
  144.     return;
  145.   }
  146.  
  147.   /* Determine how many display modes there are. */
  148.   ndmodes = 0;
  149. }
  150.  
  151. #endif
  152.  
  153. /* This routine is based on similiar code in glut_dstr.c */
  154. static DisplayMode *
  155. findMatch(DisplayMode * dmodes, int ndmodes,
  156.   Criterion * criteria, int ncriteria)
  157. {
  158.   DisplayMode *found;
  159.   int *bestScore, *thisScore;
  160.   int i, j, numok, result, worse, better;
  161.  
  162.   found = NULL;
  163.   numok = 1;            /* "num" capability is indexed from 1,
  164.                            not 0. */
  165.  
  166.   /* XXX alloca canidate. */
  167.   bestScore = (int *) malloc(ncriteria * sizeof(int));
  168.   if (!bestScore) {
  169.     __glutFatalError("out of memory.");
  170.   }
  171.   for (j = 0; j < ncriteria; j++) {
  172.     /* Very negative number. */
  173.     bestScore[j] = -32768;
  174.   }
  175.  
  176.   /* XXX alloca canidate. */
  177.   thisScore = (int *) malloc(ncriteria * sizeof(int));
  178.   if (!thisScore) {
  179.     __glutFatalError("out of memory.");
  180.   }
  181.  
  182.   for (i = 0; i < ndmodes; i++) {
  183.     if (dmodes[i].valid) {
  184.       worse = 0;
  185.       better = 0;
  186.  
  187.       for (j = 0; j < ncriteria; j++) {
  188.         int cap, cvalue, dvalue;
  189.  
  190.         cap = criteria[j].capability;
  191.         cvalue = criteria[j].value;
  192.         if (cap == NUM) {
  193.           dvalue = numok;
  194.         } else {
  195.           dvalue = dmodes[i].cap[cap];
  196.         }
  197. #ifdef TEST
  198.         if (verbose)
  199.           printf("  %s %s %d to %d\n",
  200.             capstr[cap], compstr[criteria[j].comparison], cvalue, dvalue);
  201. #endif
  202.         switch (criteria[j].comparison) {
  203.         case EQ:
  204.           result = cvalue == dvalue;
  205.           thisScore[j] = 1;
  206.           break;
  207.         case NEQ:
  208.           result = cvalue != dvalue;
  209.           thisScore[j] = 1;
  210.           break;
  211.         case LT:
  212.           result = dvalue < cvalue;
  213.           thisScore[j] = dvalue - cvalue;
  214.           break;
  215.         case GT:
  216.           result = dvalue > cvalue;
  217.           thisScore[j] = dvalue - cvalue;
  218.           break;
  219.         case LTE:
  220.           result = dvalue <= cvalue;
  221.           thisScore[j] = dvalue - cvalue;
  222.           break;
  223.         case GTE:
  224.           result = (dvalue >= cvalue);
  225.           thisScore[j] = dvalue - cvalue;
  226.           break;
  227.         case MIN:
  228.           result = dvalue >= cvalue;
  229.           thisScore[j] = cvalue - dvalue;
  230.           break;
  231.         }
  232.  
  233. #ifdef TEST
  234.         if (verbose)
  235.           printf("                result=%d   score=%d   bestScore=%d\n", result, thisScore[j], bestScore[j]);
  236. #endif
  237.  
  238.         if (result) {
  239.           if (better || thisScore[j] > bestScore[j]) {
  240.             better = 1;
  241.           } else if (thisScore[j] == bestScore[j]) {
  242.             /* Keep looking. */
  243.           } else {
  244.             goto nextDM;
  245.           }
  246.         } else {
  247.           if (cap == NUM) {
  248.             worse = 1;
  249.           } else {
  250.             goto nextDM;
  251.           }
  252.         }
  253.  
  254.       }
  255.  
  256.       if (better && !worse) {
  257.         found = &dmodes[i];
  258.         for (j = 0; j < ncriteria; j++) {
  259.           bestScore[j] = thisScore[j];
  260.         }
  261.       }
  262.       numok++;
  263.  
  264.     nextDM:;
  265.  
  266.     }
  267.   }
  268.   free(bestScore);
  269.   free(thisScore);
  270.   return found;
  271. }
  272.  
  273. /**
  274.  * Parses strings in the form of:
  275.  *  800x600
  276.  *  800x600:16
  277.  *  800x600@60
  278.  *  800x600:16@60
  279.  *  @60
  280.  *  :16
  281.  *  :16@60
  282.  * NOTE that @ before : is not parsed.
  283.  */
  284. static int
  285. specialCaseParse(char *word, Criterion * criterion, int mask)
  286. {
  287.   char *xstr, *response;
  288.   int got;
  289.   int width, height, bpp, hertz;
  290.  
  291.   switch(word[0]) {
  292.   case '0':
  293.   case '1':
  294.   case '2':
  295.   case '3':
  296.   case '4':
  297.   case '5':
  298.   case '6':
  299.   case '7':
  300.   case '8':
  301.   case '9':
  302.     /* The WWWxHHH case. */
  303.     if (mask & (1 << DM_WIDTH)) {
  304.       return -1;
  305.     }
  306.     xstr = strpbrk(&word[1], "x");
  307.     if (xstr) {
  308.       width = (int) strtol(word, &response, 0);
  309.       if (response == word || response[0] != 'x') {
  310.         /* Not a valid number OR needs to be followed by 'x'. */
  311.     return -1;
  312.       }
  313.       height = (int) strtol(&xstr[1], &response, 0);
  314.       if (response == &xstr[1]) {
  315.         /* Not a valid number. */
  316.     return -1;
  317.       }
  318.       criterion[0].capability = DM_WIDTH;
  319.       criterion[0].comparison = EQ;
  320.       criterion[0].value = width;
  321.       criterion[1].capability = DM_HEIGHT;
  322.       criterion[1].comparison = EQ;
  323.       criterion[1].value = height;
  324.       got = specialCaseParse(response,
  325.         &criterion[2], 1 << DM_WIDTH);
  326.       if (got >= 0) {
  327.         return got + 2;
  328.       } else {
  329.         return -1;
  330.       }
  331.     }
  332.     return -1;
  333.   case ':':
  334.     /* The :BPP case. */
  335.     if (mask & (1 << DM_PIXEL_DEPTH)) {
  336.       return -1;
  337.     }
  338.     bpp = (int) strtol(&word[1], &response, 0);
  339.     if (response == &word[1]) {
  340.       /* Not a valid number. */
  341.       return -1;
  342.     }
  343.     criterion[0].capability = DM_PIXEL_DEPTH;
  344.     criterion[0].comparison = EQ;
  345.     criterion[0].value = bpp;
  346.     got = specialCaseParse(response,
  347.       &criterion[1], 1 << DM_WIDTH | 1 << DM_PIXEL_DEPTH);
  348.     if (got >= 0) {
  349.       return got + 1;
  350.     } else {
  351.       return -1;
  352.     }
  353.     return -1;
  354.   case '@':
  355.     /* The @HZ case. */
  356.     if (mask & (1 << DM_HERTZ)) {
  357.       return -1;
  358.     }
  359.     hertz = (int) strtol(&word[1], &response, 0);
  360.     if (response == &word[1]) {
  361.       /* Not a valid number. */
  362.       return -1;
  363.     }
  364.     criterion[0].capability = DM_HERTZ;
  365.     criterion[0].comparison = EQ;
  366.     criterion[0].value = hertz;
  367.     got = specialCaseParse(response,
  368.       &criterion[1], ~DM_HERTZ);
  369.     if (got >= 0) {
  370.       return got + 1;
  371.     } else {
  372.       return -1;
  373.     }
  374.     return -1;
  375.   case '\0':
  376.     return 0;
  377.   }
  378.   return -1;
  379. }
  380.  
  381. /* This routine is based on similiar code in glut_dstr.c */
  382. static int
  383. parseCriteria(char *word, Criterion * criterion)
  384. {
  385.   char *cstr, *vstr, *response;
  386.   int comparator, value;
  387.  
  388.   cstr = strpbrk(word, "=><!~");
  389.   if (cstr) {
  390.     switch (cstr[0]) {
  391.     case '=':
  392.       comparator = EQ;
  393.       vstr = &cstr[1];
  394.       break;
  395.     case '~':
  396.       comparator = MIN;
  397.       vstr = &cstr[1];
  398.       break;
  399.     case '>':
  400.       if (cstr[1] == '=') {
  401.         comparator = GTE;
  402.         vstr = &cstr[2];
  403.       } else {
  404.         comparator = GT;
  405.         vstr = &cstr[1];
  406.       }
  407.       break;
  408.     case '<':
  409.       if (cstr[1] == '=') {
  410.         comparator = LTE;
  411.         vstr = &cstr[2];
  412.       } else {
  413.         comparator = LT;
  414.         vstr = &cstr[1];
  415.       }
  416.       break;
  417.     case '!':
  418.       if (cstr[1] == '=') {
  419.         comparator = NEQ;
  420.         vstr = &cstr[2];
  421.       } else {
  422.         return -1;
  423.       }
  424.       break;
  425.     default:
  426.       return -1;
  427.     }
  428.     value = (int) strtol(vstr, &response, 0);
  429.     if (response == vstr) {
  430.       /* Not a valid number. */
  431.       return -1;
  432.     }
  433.     *cstr = '\0';
  434.   } else {
  435.     comparator = NONE;
  436.   }
  437.   switch (word[0]) {
  438.   case 'b':
  439.     if (!strcmp(word, "bpp")) {
  440.       criterion[0].capability = DM_PIXEL_DEPTH;
  441.       if (comparator == NONE) {
  442.         return -1;
  443.       } else {
  444.         criterion[0].comparison = comparator;
  445.         criterion[0].value = value;
  446.         return 1;
  447.       }
  448.     }
  449.     return -1;
  450.   case 'h':
  451.     if (!strcmp(word, "height")) {
  452.       criterion[0].capability = DM_HEIGHT;
  453.       if (comparator == NONE) {
  454.         return -1;
  455.       } else {
  456.         criterion[0].comparison = comparator;
  457.         criterion[0].value = value;
  458.         return 1;
  459.       }
  460.     }
  461.     if (!strcmp(word, "hertz")) {
  462.       criterion[0].capability = DM_HERTZ;
  463.       if (comparator == NONE) {
  464.         return -1;
  465.       } else {
  466.         criterion[0].comparison = comparator;
  467.         criterion[0].value = value;
  468.         return 1;
  469.       }
  470.     }
  471.     return -1;
  472.   case 'n':
  473.     if (!strcmp(word, "num")) {
  474.       criterion[0].capability = DM_NUM;
  475.       if (comparator == NONE) {
  476.         return -1;
  477.       } else {
  478.         criterion[0].comparison = comparator;
  479.         criterion[0].value = value;
  480.         return 1;
  481.       }
  482.     }
  483.     return -1;
  484.   case 'w':
  485.     if (!strcmp(word, "width")) {
  486.       criterion[0].capability = DM_WIDTH;
  487.       if (comparator == NONE) {
  488.         return -1;
  489.       } else {
  490.         criterion[0].comparison = comparator;
  491.         criterion[0].value = value;
  492.         return 1;
  493.       }
  494.     }
  495.     return -1;
  496.   }
  497.   if (comparator == NONE) {
  498.     return specialCaseParse(word, criterion, 0);
  499.   }
  500.   return -1;
  501. }
  502.  
  503. /* This routine is based on similiar code in glut_dstr.c */
  504. static Criterion *
  505. parseDisplayString(const char *display, int *ncriteria)
  506. {
  507.   Criterion *criteria = NULL;
  508.   int n, parsed;
  509.   char *copy, *word;
  510.  
  511.   copy = __glutStrdup(display);
  512.   /* Attempt to estimate how many criteria entries should be
  513.      needed. */
  514.   n = 0;
  515.   word = strtok(copy, " \t");
  516.   while (word) {
  517.     n++;
  518.     word = strtok(NULL, " \t");
  519.   }
  520.   /* Allocate number of words of criteria.  A word
  521.      could contain as many as four criteria in the
  522.      worst case.  Example: 800x600:16@60 */
  523.   criteria = (Criterion *) malloc(4 * n * sizeof(Criterion));
  524.   if (!criteria) {
  525.     __glutFatalError("out of memory.");
  526.   }
  527.  
  528.   /* Re-copy the copy of the display string. */
  529.   strcpy(copy, display);
  530.  
  531.   n = 0;
  532.   word = strtok(copy, " \t");
  533.   while (word) {
  534.     parsed = parseCriteria(word, &criteria[n]);
  535.     if (parsed >= 0) {
  536.       n += parsed;
  537.     } else {
  538.       __glutWarning("Unrecognized game mode string word: %s (ignoring)\n", word);
  539.     }
  540.     word = strtok(NULL, " \t");
  541.   }
  542.  
  543.   free(copy);
  544.   *ncriteria = n;
  545.   return criteria;
  546. }
  547.  
  548. void APIENTRY
  549. glutGameModeString(const char *string)
  550. {
  551.   Criterion *criteria;
  552.   int ncriteria;
  553.  
  554.   initGameModeSupport();
  555.   criteria = parseDisplayString(string, &ncriteria);
  556.   currentDm = findMatch(dmodes, ndmodes, criteria, ncriteria);
  557.   free(criteria);
  558. }
  559.  
  560. int APIENTRY
  561. glutEnterGameMode(void)
  562. {
  563.   GLUTwindow *window;
  564.   int width, height;
  565.   Window win;
  566.  
  567.   if (__glutMappedMenu) {
  568.     __glutFatalUsage("entering game mode not allowed while menus in use");
  569.   }
  570.   if (__glutGameModeWindow) {
  571.     /* Already in game mode, so blow away game mode
  572.        window so apps can change resolutions. */
  573.     window = __glutGameModeWindow;
  574.     /* Setting the game mode window to NULL tricks
  575.        the window destroy code into not undoing the
  576.        screen display change since we plan on immediately
  577.        doing another mode change. */
  578.     __glutGameModeWindow = NULL;
  579.     __glutDestroyWindow(window, window);
  580.   }
  581.  
  582.   /* Assume default screen size until we find out if we
  583.      can actually change the display settings. */
  584.   width = __glutScreenWidth;
  585.   height = __glutScreenHeight;
  586.  
  587.   if (currentDm) {
  588. #ifdef _WIN32
  589.     LONG status;
  590.     static int registered = 0;
  591.  
  592.     status = ChangeDisplaySettings(¤tDm->devmode,
  593.       CDS_FULLSCREEN);
  594.     if (status == DISP_CHANGE_SUCCESSFUL) {
  595.       __glutDisplaySettingsChanged = 1;
  596.       width = currentDm->cap[DM_WIDTH];
  597.       height = currentDm->cap[DM_HEIGHT];
  598.       if (!registered) {
  599.         atexit(__glutCloseDownGameMode);
  600.         registered = 1;
  601.       }
  602.     } else {
  603.       /* Switch back to default resolution. */
  604.       ChangeDisplaySettings(NULL, 0);
  605.     }
  606. #endif
  607.   }
  608.  
  609.   window = __glutCreateWindow(NULL, 0, 0,
  610.     width, height, /* game mode */ 1);
  611.   win = window->win;
  612.  
  613. #if !defined(_WIN32)
  614.   if (__glutMotifHints == None) {
  615.     __glutMotifHints = XSGIFastInternAtom(__glutDisplay, "_MOTIF_WM_HINTS",
  616.       SGI_XA__MOTIF_WM_HINTS, 0);
  617.     if (__glutMotifHints == None) {
  618.       __glutWarning("Could not intern X atom for _MOTIF_WM_HINTS.");
  619.     }
  620.   }
  621.  
  622.   /* Game mode window is a toplevel window. */
  623.   XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1);
  624. #endif
  625.  
  626.   /* Schedule the fullscreen property to be added and to
  627.      make sure the window is configured right.  Win32
  628.      doesn't need this. */
  629.   window->desiredX = 0;
  630.   window->desiredY = 0;
  631.   window->desiredWidth = width;
  632.   window->desiredHeight = height;
  633.   window->desiredConfMask |= CWX | CWY | CWWidth | CWHeight;
  634. #ifdef _WIN32
  635.   /* Win32 does not want to use GLUT_FULL_SCREEN_WORK
  636.      for game mode because we need to be maximizing
  637.      the window in game mode, not just sizing it to
  638.      take up the full screen.  The Win32-ness of game
  639.      mode happens when you pass 1 in the gameMode parameter
  640.      to __glutCreateWindow above.  A gameMode of creates
  641.      a WS_POPUP window, not a standard WS_OVERLAPPEDWINDOW
  642.      window.  WS_POPUP ensures the taskbar is hidden. */
  643.   __glutPutOnWorkList(window,
  644.     GLUT_CONFIGURE_WORK);
  645. #else
  646.   __glutPutOnWorkList(window,
  647.     GLUT_CONFIGURE_WORK | GLUT_FULL_SCREEN_WORK);
  648. #endif
  649.  
  650.   __glutGameModeWindow = window;
  651.   return window->num + 1;
  652. }
  653.  
  654. int APIENTRY
  655. glutGameModeGet(GLenum mode)
  656. {
  657.   switch (mode) {
  658.   case GLUT_GAME_MODE_ACTIVE:
  659.     return __glutGameModeWindow != NULL;
  660.   case GLUT_GAME_MODE_POSSIBLE:
  661.     return currentDm != NULL;
  662.   case GLUT_GAME_MODE_WIDTH:
  663.     return currentDm ? currentDm->cap[DM_WIDTH] : -1;
  664.   case GLUT_GAME_MODE_HEIGHT:
  665.     return currentDm ? currentDm->cap[DM_HEIGHT] : -1;
  666.   case GLUT_GAME_MODE_PIXEL_DEPTH:
  667.     return currentDm ? currentDm->cap[DM_PIXEL_DEPTH] : -1;
  668.   case GLUT_GAME_MODE_REFRESH_RATE:
  669.     return currentDm ? currentDm->cap[DM_HERTZ] : -1;
  670.   case GLUT_GAME_MODE_DISPLAY_CHANGED:
  671.     return __glutDisplaySettingsChanged;
  672.   default:
  673.     return -1;
  674.   }
  675. }
  676.